import axios from 'axios';
import * as THREE from 'three';
import { lanToVector3, drawLabel } from '../../../utils/lan.js';
import vertexShader from '@/shader/flyLine/vertexShader.glsl';
import fragmentShader from '@/shader/flyLine/fragmentShader.glsl';
const textureLoader = new THREE.TextureLoader();

export default class {
  mesh = new THREE.Group();
  lineGroup = new THREE.Group();
  labelGroup = new THREE.Group();
  config = {
    speed: 30,
    lineConfig: {
      show: true,
      color: '#1cedf1',
      opacity: 0,
      width: 2,
      height: 100,
      length: 0.5,
      startColor: '#74e1fc',
      startOpacity: 1,
      startRandom: true,
      endColor: '#dcf0f5',
      endOpacity: 0.35
    },
    headIcon: {
      show: true,
      width: 100,
      height: 100,
      url: 'textures/aircraft.png'
    },
    circlePlaneConfig: {
      show: true,
      radius: 200,
      color: '#fff'
    },
    n: {
      show: true,
      textConfig: {
        fontFamily: 'Microsoft Yahei',
        fontSize: 30,
        color: '#FFFFFF'
      },
      basePosition: {
        x: 0.5,
        y: 0.5
      }
    }
  };
  constructor(color) {
    this.config.lineConfig.color = color;
    this.config.lineConfig.endColor = color;
    this.config.lineConfig.startColor = color;
    this.loadData();
  }
  async loadData() {
    var { data } = await axios.get('data/fly_line_data.json');
    if (data && data.length > 0) {
      data.forEach(this.createLine.bind(this));
    }
    this.mesh.add(this.labelGroup, this.lineGroup);
  }
  createLine(item) {
    const { width, height } = this.config.lineConfig;
    var material = this.createLineMaterial();
    const { startLng, startLat, endLng, endLat } = item;
    var xvect = new THREE.Vector2(Number(startLng), Number(startLat));
    var yvect = new THREE.Vector2(Number(endLng), Number(endLat));
    var _ = xvect.clone().lerp(yvect, 0.35);
    var w = xvect.clone().lerp(yvect, 0.65);
    var M = lanToVector3(startLng, startLat, 1000);
    var x = lanToVector3(endLng, endLat, 1000);
    var C = new THREE.Vector3().addVectors(M, x).multiplyScalar(0.5);
    var L = C.distanceTo(new THREE.Vector3(0, 0, 0));
    var A = (3 * M.distanceTo(x)) / 10;
    var G = (4000 + 4 * height - L) / 3;
    var D = Math.sqrt(A * A + G * G);
    var k = lanToVector3(_.x, _.y, D);
    var P = lanToVector3(w.x, w.y, D);
    var curve = new THREE.CubicBezierCurve3(M, k, P, x);
    var geometry = new THREE.TubeGeometry(curve, 50, width, 5);
    var line = new THREE.Mesh(geometry, material);
    line.userData = item;
    line.renderOrder = 10;
    this.createLineIcon(line, M.clone(), x.clone());
    this.createEndLabel(item);
    this.lineGroup.add(line);
    this.createCirclePlane(line, item);
  }
  createEndLabel(item) {
    const { endText, endLng, endLat } = item;
    const { textConfig, basePosition } = this.config.n;
    if (!endText) return;
    const scale = 1;
    var img = drawLabel(endText, textConfig);
    const map = new THREE.Texture(img);
    map.anisotropy = 16;
    map.needsUpdate = true;
    var material = new THREE.SpriteMaterial({
      map,
      depthTest: false,
      transparent: true
    });
    var mesh = new THREE.Sprite(material);
    var position = lanToVector3(endLng, endLat, 1010);
    mesh.position.copy(position);
    mesh.center = new THREE.Vector2(basePosition.x, basePosition.y);
    mesh.scale.set(img.width * scale, img.height * scale, 1);
    this.labelGroup.add(mesh);
  }
  createCirclePlane(line, item) {
    const { color, radius, show } = this.config.circlePlaneConfig;
    if (!show) return;
    const { endLng, endLat } = item;
    var J = lanToVector3(endLng, endLat, 1e3);
    const material = new THREE.ShaderMaterial({
      uniforms: {
        time: { value: 0 },
        opacity: { value: true },
        color: { value: new THREE.Color(color) }
      },
      vertexShader: `
        varying vec2 vUv;
        void main(){
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        varying vec2 vUv;
        uniform float time;
        uniform float opacity;
        uniform vec3 color;
        void main(){
          float w = 0.4;
          float r = distance(vec2(0.5), vUv);
          float calR = 0.5*time;
          if(r > calR-0.2 && r < calR) {
            if(r > 0.5 - 0.2) {
              discard;
            }else {
              float a = (r - (calR-0.2))/0.2;
              gl_FragColor = vec4(color, a * opacity);
            }
          }else {
            discard;
          }
        }
      `,
      transparent: true,
      side: THREE.BackSide,
      depthWrite: false,
      depthTest: false
    });
    var geometry = new THREE.PlaneGeometry(radius, radius, 10, 10);
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.copy(J);
    mesh.lookAt(0, 0, 0);
    line.add(mesh);
    line.userData.circlePlane = mesh;
  }
  createLineMaterial() {
    const {
      color,
      opacity,
      show,
      endColor,
      endOpacity,
      length,
      startColor,
      startOpacity,
      startRandom
    } = this.config.lineConfig;
    var random = startRandom ? Math.random() * Math.random() * 10 : 0;
    return new THREE.ShaderMaterial({
      uniforms: {
        time: { value: 0 },
        offset: { value: random },
        lineLength: { value: length },
        lineShow: { value: !!opacity && show },
        lineColor: { value: new THREE.Color(color) },
        lineOpacity: { value: opacity },
        startColor: { value: new THREE.Color(startColor) },
        startOpacity: { value: startOpacity },
        endColor: { value: new THREE.Color(endColor) },
        endOpacity: { value: endOpacity }
      },
      vertexShader,
      fragmentShader,
      transparent: true
    });
  }
  createLineIcon(line, e, t) {
    const { url, height, width, show } = this.config.headIcon;
    if (!show) return;
    var group = new THREE.Group();
    var geometry = new THREE.PlaneGeometry(width, height);
    var material = new THREE.MeshBasicMaterial({
      map: textureLoader.load(url),
      side: THREE.DoubleSide,
      transparent: true,
      depthWrite: false
    });
    var icon = new THREE.Mesh(geometry, material);
    icon.rotation.y = Math.PI;
    icon.rotation.x = Math.PI / 2;
    group.up = e.add(t).divideScalar(2).normalize();
    group.add(icon);
    line.add(group);
    return group;
  }
  update() {
    const { speed } = this.config;
    const { startColor, endColor } = this.config.lineConfig;
    this.lineGroup.children.forEach((line: any) => {
      line.material.uniforms.endColor.value = new THREE.Color(endColor);
      line.material.uniforms.startColor.value = new THREE.Color(startColor);
      line.material.uniforms.time.value = line.material.uniforms.time.value + speed;
    });
  }
  setColor(color) {
    this.config.lineConfig.color = color;
    this.config.lineConfig.endColor = color;
    this.config.lineConfig.startColor = color;
  }
}
